package com.example.websocket.redis.util;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 操作Class对象的相关操作工具类
 * @author Gaojt
 *
 */
public class ClassUtils {
	/**
	 * 取得类配置的泛型类型
	 * @param clazz 泛型类型
	 * @return 如果入参类型不是泛型类型，则返回null，否则返回对应的类型
	 */
	public static Class<?>[] getArgumentTypes(Class<?> clazz){
		Type genType = clazz.getGenericSuperclass();
		if (!(genType instanceof ParameterizedType)) {
			return null;
		}

		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		if(params == null)
			return null;
		Class<?>[] clazzs = new Class<?>[params.length];
		for (int i = 0; i < params.length; i++) {
			if (params[i] instanceof Class) {
				clazzs[i] = (Class<?>)params[i];
			}
		}
		return clazzs;
	}
	/**
	 * 验证该类是否有不带参数的构造函数
	 * @return
	 */
	public static boolean hasConstructor(Class<?> c){
		try {
			Constructor<?> constructor = c.getConstructor();
			return constructor != null;
		} catch (SecurityException e) {
			return false;
		} catch (NoSuchMethodException e) {
			return false;
		}
	}
	/**
	 * 验证是否是基础对象String，Boolean，Integer
	 * @param c
	 * @return
	 */
	public static boolean isBaseClass(Class<?> c){
		BaseType type = BaseType.classOf(c);
		return !type.equals(BaseType.OtherType);
	}
	/**
	 * 验证两个类是否有相同的非Object接口
	 * @param c1
	 * @param c2
	 * @return
	 */
	public static boolean isSuper(Class<?> superClass, Class<?> childClass){
		if(superClass.equals(childClass))
			return true;
		Set<Class<?>> superc = new HashSet<>();
		Set<Class<?>> superi = new HashSet<>();
		Class<?> tempClass = childClass;
		while(true){
			Class<?> temp = tempClass.getSuperclass();
			if(temp == null)
				break;
			if (temp.isInterface()) {
				superi.add(temp);
			} else {
				superc.add(temp);
				superi.addAll(Arrays.asList(temp.getInterfaces()));
			}
			tempClass = temp;
		}
		if(superClass.isInterface()){
			return superi.contains(superClass);
		}else{
			return superc.contains(superClass);
		}
	}
	/**
	 * 验证两个类是否在同一个域名中
	 * @param c1
	 * @param c2
	 * @return
	 */
	public static boolean isDomain(Class<?> c1, Class<?> c2){
		if(c1.isArray() || c2.isArray() || c1.isPrimitive() || c2.isPrimitive())
			return false;
		String packageName1 = c1.getPackage().getName();
		String packageName2 = c2.getPackage().getName();
		Pattern p = Pattern.compile("(\\w+\\.\\w+)\\.[\\w\\.]+");
		Matcher matcher1 = p.matcher(packageName1);
		Matcher matcher2 = p.matcher(packageName2);
		if(matcher1.matches() && matcher2.matches()){
			String domain1 = matcher1.group(1);
			String domain2 = matcher2.group(1);
			return domain1.equals(domain2);
		}
		return false;
	}
	/**
	 * 强制类型转换，如果不能转换，则置空值
	 * @param obj
	 * @param c
	 * @return
	 */
	public static <T> T forceCast(Object obj, Class<T> c){
		try {
			return cast(obj, c);
		} catch (RuntimeException e) {
			return null;
		}
	}
	/**
	 * 将对象强制转换成指定类型对象
	 * @param <T>
	 * @param c 指定类型
	 * @param obj 转换对象
	 * @return 指定类型对象
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> T cast(Object obj, Class<T> c){
		if(obj == null){
			BaseType type = BaseType.classOf(c);
			return (T)type.getValue();
		} else {
			if(obj.getClass().equals(c))//相同数据类型
				return c.cast(obj);
			if (c.equals(Object.class)) {
				return (T)obj;
			}
			BaseType type = BaseType.classOf(c);
			if(!type.equals(BaseType.OtherType)) {//基础类
				Object val;
				switch(type){
				case BooleanType:{
					val = Boolean.parseBoolean(obj.toString());
					break;
				}
				case CharacterType:{
					val = obj.toString().charAt(0);
					break;
				}
				case ByteType:{
					if (obj instanceof Number) {
						Number n = (Number)obj;
						val = n.byteValue();
					} if (obj instanceof BigDecimal) {
						BigDecimal n = (BigDecimal)obj;
						val = n.byteValue();
					} else {
						val = Byte.parseByte(obj.toString());
					}
					
					break;
				}
				case ShortType:{
					if (obj instanceof Number) {
						Number n = (Number)obj;
						val = n.shortValue();
					} if (obj instanceof BigDecimal) {
						BigDecimal n = (BigDecimal)obj;
						val = n.shortValue();
					} else {
						val = Short.parseShort(obj.toString());
					}
					break;
				}
				case IntegerType:{
					if (obj instanceof Number) {
						Number n = (Number)obj;
						val = n.intValue();
					} if (obj instanceof BigDecimal) {
						BigDecimal n = (BigDecimal)obj;
						val = n.intValue();
					} else {
						val = Integer.parseInt(obj.toString());
					}
					break;
				}
				case LongType:{
					if (obj instanceof Number) {
						Number n = (Number)obj;
						val = n.longValue();
					} if (obj instanceof BigDecimal) {
						BigDecimal n = (BigDecimal)obj;
						val = n.longValue();
					} else {
						val = Long.parseLong(obj.toString());
					}
					break;
				}
				case FloatType:{
					if (obj instanceof Number) {
						Number n = (Number)obj;
						val = n.floatValue();
					} if (obj instanceof BigDecimal) {
						BigDecimal n = (BigDecimal)obj;
						val = n.floatValue();
					} else {
						val = Float.parseFloat(obj.toString());
					}
					break;
				}
				case DoubleType:{
					if (obj instanceof Number) {
						Number n = (Number)obj;
						val = n.doubleValue();
					} if (obj instanceof BigDecimal) {
						BigDecimal n = (BigDecimal)obj;
						val = n.doubleValue();
					} else {
						val = Double.parseDouble(obj.toString());
					}
					break;
				}
				case VoidType:{
					val = null;
					break;
				}
				case StringType:{
					val = obj.toString();
					break;
				}
				case DateType:{
					val = DateUtils.parse(obj.toString());
					break;
				}
				default:{
					val = null;
					break;
				}
				}
				return (T)val;
			}else{//其他java类，复制javabean属性
				if (c.isEnum()) {//枚举类型转换
					return (T)Enum.valueOf((Class<Enum>)c, obj.toString());
				} else if(obj instanceof List) {
					List<?> l = new ArrayList<>();
					if (isSuper(List.class, c)) {
						BeanUtils.copy((List<?>)obj, l);
						return (T)l;
					} else if (c.isArray()) {
						List<?> ll = (List<?>)obj;
						Class<?> ct = c.getComponentType();
						T t = (T)ll.stream()
							.map(ele -> cast(ele, ct))
							.toArray(i -> {
								return (Object[])Array.newInstance(ct, i);
							});
						return t;
					}
				} else if(obj.getClass().isArray()) {
					Object[] array = (Object[])obj;
					if (isSuper(List.class, c)) {
						return (T)Arrays.stream(array)
								.collect(Collectors.toList());
					} else if (c.isArray()) {
						Class<?> ct = c.getComponentType();
						return (T)BeanUtils.copy(array, ct);
					}
				} else if(obj instanceof Set && isSuper(Set.class, c)) {
					Set<?> l;
					if (hasConstructor(c)) {
						try {
							l = (Set<?>)c.newInstance();
						} catch (InstantiationException e) {
							l = new HashSet<>();
						} catch (IllegalAccessException e) {
							l = new HashSet<>();
						}
					} else {
						l = new HashSet<>();
					}
					BeanUtils.copy((Set<?>)obj, l);
					return (T)l;
				} else if(obj instanceof Map && isSuper(Map.class, c)) {
					Map<?, ?> l;
					if (hasConstructor(c)) {
						try {
							l = (Map<?, ?>)c.newInstance();
						} catch (InstantiationException e) {
							l = new HashMap<>();
						} catch (IllegalAccessException e) {
							l = new HashMap<>();
						}
					} else {
						l = new HashMap<>();
					}
					BeanUtils.copy((Map<?, ?>)obj, l);
					return (T)l;
				} else if(isSuper(BigDecimal.class, c)) {
					try {
						double dv;
						if (obj instanceof Number) {
							dv = ((Number)obj).doubleValue();
						} else {
							dv = Double.parseDouble(obj.toString());
						}
						BigDecimal v = new BigDecimal(dv);
						return (T)v;
					} catch (NumberFormatException e) {
						return null;
					}
				}
				return null;
			}
		}
	}
	/**
	 * 将String转换成基本类型数据
	 * @param str 需转换的String对象
	 * @param pt 基本类型枚举
	 * @return 基本类型数据
	 */
	public static Object toBase(Object value, BaseType pt) throws NumberFormatException{
		if(value == null)//空值，转换为默认值
			pt.getValue();
		BaseType vt = BaseType.classOf(value.getClass());
		if(vt.equals(pt))//相同数据类型，直接转换
			return pt.getType().cast(value);
		//不同数据类型，以String作为中间值转换
		String str = value.toString();
		switch(pt){
		case BooleanType:{
			return Boolean.parseBoolean(str);
		}
		case CharacterType:{
			return str.charAt(0);
		}
		case ByteType:{
			return Byte.parseByte(str);
		}
		case ShortType:{
			return Short.parseShort(str);
		}
		case IntegerType:{
			return Integer.parseInt(str);
		}
		case LongType:{
			return Long.parseLong(str);
		}
		case FloatType:{
			return Float.parseFloat(str);
		}
		case DoubleType:{
			return Double.parseDouble(str);
		}
		case VoidType:{
			return null;
		}
		case StringType:{
			return str;
		}
		case DateType:{
			return DateUtils.parse(str);
		}
		default: return null;
		}
	}
	/**
	 * 获取类中指定方法名的方法
	 * @param c 类
	 * @param methodName 方法名
	 * @return 方法集
	 */
	public static List<Method> getMethods(Class<?> c, String methodName){
		Method[] ms = c.getMethods();
		List<Method> list = new ArrayList<Method>();
		for (int i = 0; i < ms.length; i++) {
			if(ms[i].getName().equals(methodName))
				list.add(ms[i]);
		}
		return list;
	}
}
